package com.fancy.middleware.common.service.redis;

import com.fancy.middleware.common.constant.ExceptionConstant;
import com.fancy.middleware.common.exception.MsgException;
import com.fancy.middleware.common.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@SuppressWarnings("ALL")
@Component
@Slf4j
public class RedisOperationService {

    private RedisUtils redisUtils;

    public RedisUtils getRedisUtils() {
        return redisUtils;
    }

    public RedisOperationService(@Autowired @Qualifier("redisTemplate") RedisTemplate<String, Object> redisTemplate) {
        redisUtils = new RedisUtils(redisTemplate);
    }

    public Object lockAndExpire(String lockKey, long expire, Supplier<Object> supplier) throws MsgException {
        long timeMillis = System.currentTimeMillis();
        // TODO 1000 需要配置
        expire = expire <= 0 ? 1000 : expire;
        Boolean setnxFlag = false;
        // 自旋
        while (true) {
            try {
                if (redisUtils.setnxAndExpire(lockKey, 1, expire)) {
                    log.debug("lock key {}", lockKey);
                    setnxFlag = true;
                    return supplier.get();
                } else {
                    // TODO 2000 需要做配置  数据需要调式确认
                    if (System.currentTimeMillis() - timeMillis >= 2000) {
                        log.error("lock key:{} timeout {}", lockKey, 2000);
                        throw new MsgException(ExceptionConstant.System.LOCK_TIMEOUT, ExceptionConstant.System.LOCK_TIMEOUT_MSG);
                    }
                    //  睡眠 防止CPU 过高 TODO 100L 需要配置调试确认
                    TimeUnit.MICROSECONDS.sleep(100L);
                }
            } catch (InterruptedException e) {
                log.error("InterruptedException: {}", e);
                throw new MsgException(e);
            } finally {
                //setnx 成功 释放锁
                if (setnxFlag) {
                    log.debug("delete lock key {}", lockKey);
                    redisUtils.delete(lockKey);
                }
            }
        }
    }
}

